var canvas;
var gl;
var program;

var modelViewMatrix = mat4();
var modelViewLocation;
var projectMatrix = mat4();
var projectMatrixLocation;
var normalMatrix = mat4();
var normalMatrixLocation;

var pointsToDraw = [];
var indicesToDraw = [];
var normalOfPoints = []; // 法向量
var shineOfPoints = []; // 光泽度
var kaOfPoints = []; // K_a
var kdOfPoints = []; // K_d
var ksOfPoints = []; // K_s
var abuffer;
var dbuffer;
var sbuffer;
var aLocation;
var dLocation;
var sLocation;
var lightPosition = vec4(0,1,0.7,0); // 光源位置
var lightPositionLocation;
var lightA = vec4(0.6,0.6,0.6,1); // ambient
var lightD = vec4(1,1,1,1); // diffuse
var lightS = vec4(1,1,1,1); // specular

// texture: 1=a, 2=d, 3=s, 4=shine
var texture1 = vec4(1,0.4,1,60);
var texture2 = vec4(1,0.1,1,150);

var vTexCoords = []; // 顶点纹理坐标
var vNormalCoords = []; // 法线纹理坐标
var tangentOfPoints = []; // 顶点切向量

// 交互
var keyEvent;
var eye = vec3(0,1,-2); // 视点
var cameraAngle = -90; // 相机当前角度
var buttonEvent;
var lightAngle = 0; // 光源当前角度
var move = false;

window.onload = init;

function init() {
    canvas = $("#gl-canvas")[0];
    // 初始化WebGL上下文
    gl = WebGLUtils.setupWebGL(canvas);
  
    // 确认WebGL支持性
    if (!gl) {
      alert("无法初始化WebGL，你的浏览器、操作系统或硬件等可能不支持WebGL。");
      return;
    }
  
    // 设置视口
    gl.viewport(0, 0, canvas.width, canvas.height);
    // 清除所有图像
    gl.clearColor(0.53, 0.81, 0.92, 1.0);
    
    gl.enable(gl.DEPTH_TEST);

    // 加载着色器
    program = initShaders(gl, "vertex-shader", "fragment-shader");
    gl.useProgram(program);

    // 绘制元素
    drawScene();

    // 发送至GPU
    bufferPoints();

    // 初始化相机
    initCamera();

    // 渲染
    render();

    // 绑定事件
    /*document.onmousemove = mouseMoveListener;
    canvas.onmousedown = mouseDownListener;
    document.onmouseup = mouseUpListener;
    canvas.onmousewheel = mouseWheelListener;*/
    document.onkeydown = keyDownListener;
    document.onkeyup = keyUpListener;
    move = true;
    buttonEvent = setInterval(function() {
        lightAngle -= 10;
        lightPosition[1] = Math.cos(radians(lightAngle));
        lightPosition[0] = Math.sin(radians(lightAngle));
        render();
    }, 100);
}

function render() {
    // 清除缓冲区
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // 设置uniform变量
    gl.uniformMatrix4fv(modelViewLocation, false, flatten(modelViewMatrix));
    normalMatrix = inverse(transpose(modelViewMatrix));
    gl.uniformMatrix4fv(normalMatrixLocation, false, flatten(normalMatrix));
    gl.uniformMatrix4fv(projectMatrixLocation, false, flatten(projectMatrix));
    gl.uniform4fv(lightPositionLocation, flatten(lightPosition));

    


    // 绘制
    gl.drawElements(gl.TRIANGLES, indicesToDraw.length, gl.UNSIGNED_SHORT, 0);
}

function bufferPoints() {
    // 写入顶点
    // 绑定缓冲区
    var vbuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
    // 写入数据
    gl.bufferData(gl.ARRAY_BUFFER, flatten(pointsToDraw), gl.STATIC_DRAW);
    // 设置着色器变量
    var vLocation = gl.getAttribLocation(program, "vPosition");
    gl.vertexAttribPointer(vLocation, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(vLocation);




    // 写入光学材质
    // 设置明暗参数
    // 创建buffer
    abuffer = gl.createBuffer();
    dbuffer = gl.createBuffer();
    sbuffer = gl.createBuffer();
    aLocation = gl.getAttribLocation(program, "aProduct");
    dLocation = gl.getAttribLocation(program, "dProduct");
    sLocation = gl.getAttribLocation(program, "sProduct");
    // k_a
    gl.bindBuffer(gl.ARRAY_BUFFER, abuffer);
    var aProduct = mult4Elements(kaOfPoints, lightA);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(aProduct), gl.STATIC_DRAW);
    gl.vertexAttribPointer(aLocation, 4, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(aLocation);

    // k_d
    gl.bindBuffer(gl.ARRAY_BUFFER, dbuffer);
    var dProduct = mult4Elements(kdOfPoints, lightD);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(dProduct), gl.STATIC_DRAW);
    gl.vertexAttribPointer(dLocation, 4, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(dLocation);

    // k_s
    gl.bindBuffer(gl.ARRAY_BUFFER, sbuffer);
    var sProduct = mult4Elements(ksOfPoints, lightS);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(sProduct), gl.STATIC_DRAW);
    gl.vertexAttribPointer(sLocation, 4, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(sLocation);

    // shine
    var shinebuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, shinebuffer);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(shineOfPoints), gl.STATIC_DRAW);
    var shineLocation = gl.getAttribLocation(program, "shininess");
    gl.vertexAttribPointer(shineLocation, 1, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(shineLocation);

    // 法向量
    var nbuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, nbuffer);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(normalOfPoints), gl.STATIC_DRAW);
    var normalLocation = gl.getAttribLocation(program, "vNormal");
    gl.vertexAttribPointer(normalLocation, 4, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(normalLocation);



    // 贴图设置
    var texture = gl.createTexture();
    var image = new Image();
    image.src = "resources/texture.jpg";
    image.addEventListener('load', function() {
        // 现在图像加载完成，拷贝到纹理中
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
        gl.generateMipmap(gl.TEXTURE_2D);
        gl.uniform1i(gl.getUniformLocation(program, "texMap"), 0);
        render();
    });

    // 法线贴图
    var normalTexture = gl.createTexture();
    var normalImage = new Image();
    normalImage.src = "resources/bumpmap_NRM.jpg";
    normalImage.addEventListener('load', function() {
        // 现在图像加载完成，拷贝到纹理中
        gl.activeTexture(gl.TEXTURE1);
        gl.bindTexture(gl.TEXTURE_2D, normalTexture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, normalImage);
        gl.generateMipmap(gl.TEXTURE_2D);
        gl.uniform1i(gl.getUniformLocation(program, "normalMap"), 1);
        render();
    });



    // 写入材质坐标
    uvBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(vTexCoords), gl.STATIC_DRAW);
    var vTexCoordLocation = gl.getAttribLocation(program, "vTexCoord");
    gl.vertexAttribPointer(vTexCoordLocation, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(vTexCoordLocation);
    

    // 写入法线贴图坐标
    nuvBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, nuvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(vNormalCoords), gl.STATIC_DRAW);
    var vNormalCoordLocation = gl.getAttribLocation(program, "vNormalCoord");
    gl.vertexAttribPointer(vNormalCoordLocation, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(vNormalCoordLocation);

    // 写入切向量
    tanBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, tanBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(tangentOfPoints), gl.STATIC_DRAW);
    var vTanLocation = gl.getAttribLocation(program, "vTan");
    gl.vertexAttribPointer(vTanLocation, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(vTanLocation);

    // 写入索引
    // 绑定缓冲区
    var ibuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
    // 写入数据
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indicesToDraw), gl.STATIC_DRAW);

    // 设置uniform变量位置
    modelViewLocation = gl.getUniformLocation(program, "modelViewMatrix");
    projectMatrixLocation = gl.getUniformLocation(program, "projectMatrix");
    lightPositionLocation = gl.getUniformLocation(program, "lightPosition");
    normalMatrixLocation = gl.getUniformLocation(program, "normalMatrix");
    
}

function drawScene() {
    // define basic operation
    var r = rotate(-90, vec3(1,0,0));

    marbleTexOrigin = vec2(0, 2096/4096);
    marbleTexLength = 2000/4096;
    emptyTexOrigin = vec2(0,0);
    emptyTexLength = 600/4096;
    bkTexOrigin = vec2(0, 610/4096);
    bkTexLength = 1400/4096;

    emptyNormalOrigin = vec2(0,100/2048);
    emptyNormalLength = 100/2048;
    bumpNormalOrigin = vec2(0, 1024/2048);
    bumpNormalLength = 1024/2048;

    // the first snowman
    // draw the big ball
    var ball1 = sphere(vec3(-0.75,0,-0.4), 0.4, vec4(1,1,1,1), vec4(1,1,1,1), texture1, marbleTexOrigin, marbleTexLength);
	
    //draw the small ball
	var ball2 = sphere(vec3(-0.75,0,0.15), 0.25, vec4(1,1,1,1), vec4(1,1,1,1), texture1, marbleTexOrigin, marbleTexLength);
    // draw her hat
    var hat = cone(vec3(-0.75,0,0.35), 0.15, 0.3, vec4(1,0.2,0.2,1), vec4(1,0.75,0.8,1), texture1, emptyTexOrigin, emptyTexLength);

    // draw the small ball on her hat
    var ball3 = sphere(vec3(-0.75,0,0.65), 0.04, vec4(1,1,1,1), vec4(1,1,1,1), texture1, emptyTexOrigin, emptyTexLength);

    // draw her nose
    var nose = cone(vec3(-0.75,-0.15,0.2), 0.04, 0.25, vec4(1,0.55,0,1), vec4(1,0.2,0.2,1), texture1, emptyTexOrigin, emptyTexLength);
    mult4object(r, nose);

    // draw her eyes
    var l_eye = sphere(vec3(-0.85,0.22,0.21), 0.02, vec4(0,0,0,1), vec4(0,0,0,1), texture1, emptyTexOrigin, emptyTexLength);
    var r_eye = sphere(vec3(-0.65,0.22,0.21), 0.02, vec4(0,0,0,1), vec4(0,0,0,1), texture1, emptyTexOrigin, emptyTexLength);

    // draw her hands
    var l_hand = cuboid(0.015, 0.015, 0.6, vec4(0.54,0.27,0.07,1), texture1, emptyTexOrigin, emptyTexLength, emptyNormalOrigin, emptyNormalLength);
    var r_l = rotate(45, vec3(0,1,0));
    mult4object(r_l, l_hand);
    var t_l = translate(-0.45,0,-0.1);
    mult4object(t_l, l_hand);

    var r_hand = cuboid(0.015, 0.015, 0.6, vec4(0.54,0.27,0.07,1), texture1, emptyTexOrigin, emptyTexLength, emptyNormalOrigin, emptyNormalLength);
    var r_r = rotate(-45, vec3(0,1,0));
    mult4object(r_r, r_hand);
    var t_r = translate(-1.05,0,-0.1);
	mult4object(t_r, r_hand);
	
	
	// the second snowman
	// draw the first cube

	var cube1 = cuboid(0.65, 0.65, 0.65, vec4(1,1,1,1), texture2, emptyTexOrigin, emptyTexLength, bumpNormalOrigin, bumpNormalLength);
	var t_cube1 = translate(0.75, 0, -0.4);
	mult4object(t_cube1, cube1);
	// draw the second cube 
	var cube2 = cuboid(0.45, 0.45, 0.45, vec4(1,1,1,1), texture2, emptyTexOrigin, emptyTexLength, bumpNormalOrigin, bumpNormalLength);
	var t_cube2 = translate(0.75, 0, 0.15);
	mult4object(t_cube2, cube2);
	
	// draw the hat
	var hat2 = cone(vec3(0.75,0,0.35), 0.15, 0.3, vec4(1,0.2,0.2,1), vec4(1,0.75,0.8,1), texture2, emptyTexOrigin, emptyTexLength);
	
	// draw the ball on its hat
	var ball4 = sphere(vec3(0.75,0,0.65), 0.04, vec4(1,1,1,1), vec4(1,1,1,1), texture2, emptyTexOrigin, emptyTexLength);
	
	// draw her nose
    var nose2 = cone(vec3(0.75,-0.15,0.2), 0.04, 0.25, vec4(1,0.55,0,1), vec4(1,0.2,0.2,1), texture2, emptyTexOrigin, emptyTexLength);
    mult4object(r, nose2);
	
	// draw the eyes
    var l_eye2 = cuboid(0.05, 0.05, 0.05, vec4(0, 0, 0, 1), texture2, emptyTexOrigin, emptyTexLength, emptyNormalOrigin, emptyNormalLength);
	var t_l_eye2 = translate(0.65, 0.22, 0.21);
	mult4object(t_l_eye2, l_eye2);
	var r_eye2 = cuboid(0.05, 0.05, 0.05, vec4(0, 0, 0, 1), texture2, emptyTexOrigin, emptyTexLength, emptyNormalOrigin, emptyNormalLength);
	var t_r_eye2 = translate(0.85, 0.22, 0.21);
	mult4object(t_r_eye2, r_eye2);
	
	var l_hand2 = cuboid(0.015, 0.015, 0.6, vec4(0.54,0.27,0.07,1), texture2, emptyTexOrigin, emptyTexLength, emptyNormalOrigin, emptyNormalLength);
    var r_l2 = rotate(45, vec3(0,1,0));
    mult4object(r_l2, l_hand2);
    var t_l2 = translate(1.05,0,-0.1);
    mult4object(t_l2, l_hand2);

    var r_hand2 = cuboid(0.015, 0.015, 0.6, vec4(0.54,0.27,0.07,1), texture2, emptyTexOrigin, emptyTexLength, emptyNormalOrigin, emptyNormalLength);
    var r_r2 = rotate(-45, vec3(0,1,0));
    mult4object(r_r2, r_hand2);
    var t_r2 = translate(0.45,0,-0.1);
	mult4object(t_r2, r_hand2);
    
    // final rotate
    mult4object(r, pointsToDraw);

    // backgroud
    var bklength = 100;
    var height = -0.8;
    var numOfPoints = pointsToDraw.length;
    pointsToDraw.push(vec3(-bklength/2, height, -bklength/2));
    pointsToDraw.push(vec3(-bklength/2, height, bklength/2));
    pointsToDraw.push(vec3(bklength/2, height, bklength/2));
    pointsToDraw.push(vec3(bklength/2, height, -bklength/2));
    vTexCoords.push(vec2(bkTexOrigin[0], bkTexOrigin[1]));
    vTexCoords.push(vec2(bkTexOrigin[0], bkTexOrigin[1]+bkTexLength));
    vTexCoords.push(vec2(bkTexOrigin[0]+bkTexLength, bkTexOrigin[1]+bkTexLength));
    vTexCoords.push(vec2(bkTexOrigin[0]+bkTexLength, bkTexOrigin[1]));
    for (var i=0; i<4; i++) {
        normalOfPoints.push(vec4(0,0,1,0));
        kaOfPoints.push(vec4(1.5,1.5,1.5,1));
        kdOfPoints.push(vec4(0.2,0.2,0.2,1));
        ksOfPoints.push(vec4(0,0,0,1));
        shineOfPoints.push(0);
        vNormalCoords.push(vec2(0,20/1024));
        tangentOfPoints.push(vec3(1,0,0));
    }
    indicesToDraw.push(numOfPoints + 0);
    indicesToDraw.push(numOfPoints + 1);
    indicesToDraw.push(numOfPoints + 2);
    indicesToDraw.push(numOfPoints + 0);
    indicesToDraw.push(numOfPoints + 2);
    indicesToDraw.push(numOfPoints + 3);
}

function initCamera() {
    modelViewMatrix = lookAt(eye, vec3(0,0,0), vec3(0,1,0));
    projectMatrix = perspective(60, 1, 0.3, 45);
    //projectMatrix = ortho(-1.5, 1.5, -1.5, 1.5, 1, -1);
}

function keyDownListener(event) {
    var dAngle = 3;
    switch(event.keyCode){
        case 37: //左
        clearInterval(keyEvent);
        keyEvent = setInterval(function(){
            cameraAngle -= dAngle;
            eye[0] = 2*Math.cos(radians(cameraAngle));
            eye[2] = 2*Math.sin(radians(cameraAngle));
            modelViewMatrix = lookAt(eye, vec3(), vec3(0,1,0));
            render();
        }, 10);
        break; 
        case 39:  //右
        clearInterval(keyEvent);
        keyEvent = setInterval(function(){
            cameraAngle += dAngle;
            eye[0] = 2*Math.cos(radians(cameraAngle));
            eye[2] = 2*Math.sin(radians(cameraAngle));
            modelViewMatrix = lookAt(eye, vec3(), vec3(0,1,0));
            render();
        }, 10);
        break;
    }
}

function keyUpListener(event) {
    clearInterval(keyEvent);
}

function lightMove() {
    if (move==false) {
        move = true;
        buttonEvent = setInterval(function() {
            lightAngle -= 10;
            lightPosition[1] = Math.cos(radians(lightAngle));
            lightPosition[0] = Math.sin(radians(lightAngle));
            render();
        }, 100);
    } else {
        move = false;
        clearInterval(buttonEvent);
    }
}


function sphere(center, radius, north_color, south_color, texture, texMapOrigin, texMapLength) { // 绘制球面
    var precision = 100;
    var numOfPoints = pointsToDraw.length;

    // 生成球面点
    for (var i = 0; i < precision; i++) {
        for (var j = 0; j < precision+1; j++) {
            var theta = 2*Math.PI*i/precision;
            var phi = Math.PI*j/precision;
            pointsToDraw.push(vec3(
                center[0] + radius * Math.sin(phi) * Math.cos(theta),
                center[1] + radius * Math.sin(phi) * Math.sin(theta), 
                center[2] + radius * Math.cos(phi)
            ));

            kaOfPoints.push(vec4(scale(texture[0], vec3(
                north_color[0] * (precision - j)/precision + south_color[0] * j/precision,
                north_color[1] * (precision - j)/precision + south_color[1] * j/precision,
                north_color[2] * (precision - j)/precision + south_color[2] * j/precision)),
                north_color[3] * (precision - j)/precision + south_color[3] * j/precision)
            );

            kdOfPoints.push(vec4(scale(texture[1], vec3(
                north_color[0] * (precision - j)/precision + south_color[0] * j/precision,
                north_color[1] * (precision - j)/precision + south_color[1] * j/precision,
                north_color[2] * (precision - j)/precision + south_color[2] * j/precision)),
                north_color[3] * (precision - j)/precision + south_color[3] * j/precision)
            );

            ksOfPoints.push(vec4(scale(texture[2], vec3(
                north_color[0] * (precision - j)/precision + south_color[0] * j/precision,
                north_color[1] * (precision - j)/precision + south_color[1] * j/precision,
                north_color[2] * (precision - j)/precision + south_color[2] * j/precision)),
                north_color[3] * (precision - j)/precision + south_color[3] * j/precision)
            );

            shineOfPoints.push(texture[3]);
            normalOfPoints.push(vec4(
                Math.sin(phi) * Math.cos(theta),
                Math.sin(phi) * Math.sin(theta),
                Math.cos(phi),
                0)
            );

            vTexCoords.push(vec2(
                texMapOrigin[0] + texMapLength * i/precision,
                texMapOrigin[1] + texMapLength * j/precision
            ));
            vNormalCoords.push(vec2(0,20/1024));
            tangentOfPoints.push(vec3(
                -Math.sin(theta),
                Math.cos(theta),
                0
            ));

        }
    }


    // 生成绘图索引
    for (var i=0; i<precision; i++) {
        offsetNextLine = (i==precision-1)?0:(i+1);

        indicesToDraw.push(numOfPoints + i*(precision+1) + 0);
        indicesToDraw.push(numOfPoints + i*(precision+1) + 1);
        indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + 1);

        indicesToDraw.push(numOfPoints + i*(precision+1) + precision);
        indicesToDraw.push(numOfPoints + i*(precision+1) + precision-1);
        indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + precision-1);       

        for (var j=1; j<precision-1; j++) {
            indicesToDraw.push(numOfPoints + i*(precision+1) + j);
            indicesToDraw.push(numOfPoints + i*(precision+1) + j+1);
            indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + j);

            indicesToDraw.push(numOfPoints + i*(precision+1) + j+1);
            indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + j);
            indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + j+1);
        }
    }

    // 将坐标返回
    return pointsToDraw.slice(numOfPoints).concat(normalOfPoints.slice(numOfPoints));
}

function cone(center, radius, height, top_color, bottom_color, texture, texMapOrigin, texMapLength) { // 绘制圆锥
    var precision = 100;
    var numOfPoints = pointsToDraw.length;
    

    // 生成锥面点
    // 顶点
    for (var i=0; i<precision; i++) {
        pointsToDraw.push(vec3(
            center[0],
            center[1],
            center[2]+height
        ));
        kaOfPoints.push(vec4(scale(texture[0], vec3(top_color)), top_color[3]));
        kdOfPoints.push(vec4(scale(texture[1], vec3(top_color)), top_color[3]));
        ksOfPoints.push(vec4(scale(texture[2], vec3(top_color)), top_color[3]));
        shineOfPoints.push(texture[3]);
        var theta = 2*Math.PI*i/precision;
        normalOfPoints.push(vec4(
            Math.cos(theta),
            Math.sin(theta),
            radius/height*Math.sqrt(Math.cos(theta)*Math.cos(theta)+Math.sin(theta)*Math.sin(theta)),
            0)
        );
        vTexCoords.push(vec2(texMapOrigin[0]+0.5*texMapLength, texMapOrigin[1]+0.5*texMapLength));
        vNormalCoords.push(vec2(0,20/1024));
        tangentOfPoints.push(vec3(
            -Math.sin(theta),
            Math.cos(theta),
            height/radius
        ));
        
    }
    // 底面中心
    pointsToDraw.push(center);
    kaOfPoints.push(vec4(scale(texture[0], vec3(bottom_color)), bottom_color[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(bottom_color)), bottom_color[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(bottom_color)), bottom_color[3]));
    shineOfPoints.push(texture[3]);
    normalOfPoints.push(vec4(0,0,-1,0));
    vTexCoords.push(vec2(texMapOrigin[0]+0.5*texMapLength, texMapOrigin[1]+0.5*texMapLength));
    vNormalCoords.push(vec2(0,20/1024));
    tangentOfPoints.push(vec3(1,0,0));

    // 底面
    for (var i = 0; i < precision; i++) {
        var theta = 2*Math.PI*i/precision;
        pointsToDraw.push(vec3(
            center[0] + radius * Math.cos(theta),
            center[1] + radius * Math.sin(theta),
            center[2]
        ));
        kaOfPoints.push(vec4(scale(texture[0], vec3(bottom_color)), bottom_color[3]));
        kdOfPoints.push(vec4(scale(texture[1], vec3(bottom_color)), bottom_color[3]));
        ksOfPoints.push(vec4(scale(texture[2], vec3(bottom_color)), bottom_color[3]));
        shineOfPoints.push(texture[3]);
        normalOfPoints.push(vec4(
            Math.cos(theta),
            Math.sin(theta),
            radius/height*Math.sqrt(Math.cos(theta)*Math.cos(theta)+Math.sin(theta)*Math.sin(theta)),
            0)
        );
        var l = Math.sqrt(radius*radius + height*height);
        vTexCoords.push(vec2(
            texMapOrigin[0] + texMapLength * l * Math.cos(2*Math.PI*i/precision*radius/l),
            texMapOrigin[1] + texMapLength * l * Math.sin(2*Math.PI*i/precision*radius/l)
        ));
        vNormalCoords.push(vec2(0,20/1024));
        tangentOfPoints.push(vec3(
            -Math.sin(theta),
            Math.cos(theta),
            height/radius
        ));
    }

    // 生成绘图索引
    for (var i=0; i<precision; i++) {
        offsetNext = (i==precision-1)?0:(i+1);

        indicesToDraw.push(numOfPoints + i);
        indicesToDraw.push(numOfPoints + precision+1 + i);
        indicesToDraw.push(numOfPoints + precision+1 + offsetNext);

        indicesToDraw.push(numOfPoints + precision+1);
        indicesToDraw.push(numOfPoints + precision+1 + i);
        indicesToDraw.push(numOfPoints + precision+1 + offsetNext);
    }


    // 将坐标返回
    return pointsToDraw.slice(numOfPoints).concat(normalOfPoints.slice(numOfPoints));
}

function cuboid(x,y,z,color, texture, texMapOrigin, texMapLength, normalMapOrigin, normalMapLength) { // 绘制长方体
    var numOfPoints = pointsToDraw.length;
    

    // 生成长方体顶点
    pointsToDraw = pointsToDraw.concat([
        // z=-z/2
        vec3(-x/2, -y/2, -z/2),
        vec3(+x/2, -y/2, -z/2),
        vec3(+x/2, +y/2, -z/2),
        vec3(-x/2, +y/2, -z/2),

        // z=+z/2
        vec3(-x/2, -y/2, +z/2),
        vec3(+x/2, -y/2, +z/2),
        vec3(+x/2, +y/2, +z/2),
        vec3(-x/2, +y/2, +z/2),

        // x=+x/2
        vec3(+x/2, -y/2, +z/2),
        vec3(+x/2, -y/2, -z/2),
        vec3(+x/2, +y/2, -z/2),
        vec3(+x/2, +y/2, +z/2),

        // x=-x/2
        vec3(-x/2, -y/2, +z/2),
        vec3(-x/2, -y/2, -z/2),
        vec3(-x/2, +y/2, -z/2),
        vec3(-x/2, +y/2, +z/2),

        // y=+y/2
        vec3(+x/2, +y/2, +z/2),
        vec3(+x/2, +y/2, -z/2),
        vec3(-x/2, +y/2, -z/2),
        vec3(-x/2, +y/2, +z/2),

        // y=-y/2
        vec3(+x/2, -y/2, +z/2),
        vec3(+x/2, -y/2, -z/2),
        vec3(-x/2, -y/2, -z/2),
        vec3(-x/2, -y/2, +z/2),
    ]);

    vTexCoords = vTexCoords.concat([
        // z=-z/2
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]),
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]),

        // z=+z/2
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]+texMapLength*0.75),
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]+texMapLength*0.5),
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]+texMapLength*0.5),
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]+texMapLength*0.75),

        // x=+x/2
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]+texMapLength*0.5),
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]+texMapLength*0.5),

        // x=-x/2
        vec2(texMapOrigin[0]+texMapLength*1, texMapOrigin[1]+texMapLength*0.5),
        vec2(texMapOrigin[0]+texMapLength*1, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.75, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.75, texMapOrigin[1]+texMapLength*0.5),

        // y=+y/2
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]+texMapLength*0.5),
        vec2(texMapOrigin[0]+texMapLength*0.5, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.75, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0]+texMapLength*0.75, texMapOrigin[1]+texMapLength*0.5),

        // y=-y/2
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]+texMapLength*0.5),
        vec2(texMapOrigin[0]+texMapLength*0.25, texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0], texMapOrigin[1]+texMapLength*0.25),
        vec2(texMapOrigin[0], texMapOrigin[1]+texMapLength*0.5),
    ]);

    vNormalCoords = vNormalCoords.concat([
        // z=-z/2
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]),
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]),

        // z=+z/2
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]+normalMapLength*0.75),
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]+normalMapLength*0.5),
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]+normalMapLength*0.5),
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]+normalMapLength*0.75),

        // x=+x/2
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]+normalMapLength*0.5),
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]+normalMapLength*0.5),

        // x=-x/2
        vec2(normalMapOrigin[0]+normalMapLength*1, normalMapOrigin[1]+normalMapLength*0.5),
        vec2(normalMapOrigin[0]+normalMapLength*1, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.75, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.75, normalMapOrigin[1]+normalMapLength*0.5),

        // y=+y/2
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]+normalMapLength*0.5),
        vec2(normalMapOrigin[0]+normalMapLength*0.5, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.75, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0]+normalMapLength*0.75, normalMapOrigin[1]+normalMapLength*0.5),

        // y=-y/2
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]+normalMapLength*0.5),
        vec2(normalMapOrigin[0]+normalMapLength*0.25, normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0], normalMapOrigin[1]+normalMapLength*0.25),
        vec2(normalMapOrigin[0], normalMapOrigin[1]+normalMapLength*0.5),
    ]);


    for (var i=0; i<24; i++) {
        kaOfPoints.push(vec4(scale(texture[0], vec3(color)), color[3]));
        kdOfPoints.push(vec4(scale(texture[1], vec3(color)), color[3]));
        ksOfPoints.push(vec4(scale(texture[2], vec3(color)), color[3]));
        shineOfPoints.push(texture[3]);
    }

    for (var i=0; i<4; i++) {
        normalOfPoints.push(vec4(0,0,-1,0));
        tangentOfPoints.push(vec3(-1,0,0));
    }
    for (var i=0; i<4; i++) {
        normalOfPoints.push(vec4(0,0,1,0));
        tangentOfPoints.push(vec3(1,0,0));
    }
    for (var i=0; i<4; i++) {
        normalOfPoints.push(vec4(1,0,0,0));
        tangentOfPoints.push(vec3(0,0,-1));
    }
    for (var i=0; i<4; i++) {
        normalOfPoints.push(vec4(-1,0,0,0));
        tangentOfPoints.push(vec3(0,0,-1));
    }
    for (var i=0; i<4; i++) {
        normalOfPoints.push(vec4(0,1,0,0));
        tangentOfPoints.push(vec3(0,0,-1));
    }
    for (var i=0; i<4; i++) {
        normalOfPoints.push(vec4(0,-1,0,0));
        tangentOfPoints.push(vec3(0,0,-1));
    }

    // 生成绘图索引
    indicesToDraw = indicesToDraw.concat([
        numOfPoints+0, numOfPoints+1, numOfPoints+2,
        numOfPoints+0, numOfPoints+2, numOfPoints+3,
        numOfPoints+4, numOfPoints+5, numOfPoints+6,
        numOfPoints+4, numOfPoints+6, numOfPoints+7,
        numOfPoints+8, numOfPoints+9, numOfPoints+10,
        numOfPoints+8, numOfPoints+10, numOfPoints+11,
        numOfPoints+12, numOfPoints+13, numOfPoints+14,
        numOfPoints+12, numOfPoints+14, numOfPoints+15,
        numOfPoints+16, numOfPoints+17, numOfPoints+18,
        numOfPoints+16, numOfPoints+18, numOfPoints+19,
        numOfPoints+20, numOfPoints+21, numOfPoints+22,
        numOfPoints+20, numOfPoints+22, numOfPoints+23,
    ]);

    // 返回坐标
    return pointsToDraw.slice(numOfPoints).concat(normalOfPoints.slice(numOfPoints));
}

function triangularPyramid(p1, p2, p3, p4, c1, c2, c3, c4, texture) { // 绘制四面体
    var numOfPoints = pointsToDraw.length;
    
    c1 = vec4(c1);
    c2 = vec4(c2);
    c3 = vec4(c3);
    c4 = vec4(c4);

    // 生成四面体顶点
    pointsToDraw = pointsToDraw.concat([
        p1, p2, p3,
        p1, p3, p4,
        p1, p2, p4,
        p2, p3, p4
    ]);

    for (var j=0; j<12; j++) {
        shineOfPoints.push(texture[3]);
    }

    kaOfPoints.push(vec4(scale(texture[0], vec3(c1)), c1[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c1)), c1[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c1)), c1[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c2)), c2[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c2)), c2[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c2)), c2[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c3)), c3[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c3)), c3[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c3)), c3[3]));


    kaOfPoints.push(vec4(scale(texture[0], vec3(c1)), c1[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c1)), c1[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c1)), c1[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c3)), c3[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c3)), c3[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c3)), c3[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c4)), c4[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c4)), c4[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c4)), c4[3]));


    kaOfPoints.push(vec4(scale(texture[0], vec3(c1)), c1[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c1)), c1[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c1)), c1[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c2)), c2[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c2)), c2[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c2)), c2[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c4)), c4[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c4)), c4[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c4)), c4[3]));


    kaOfPoints.push(vec4(scale(texture[0], vec3(c2)), c2[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c2)), c2[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c2)), c2[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c3)), c3[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c3)), c3[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c3)), c3[3]));

    kaOfPoints.push(vec4(scale(texture[0], vec3(c4)), c4[3]));
    kdOfPoints.push(vec4(scale(texture[1], vec3(c4)), c4[3]));
    ksOfPoints.push(vec4(scale(texture[2], vec3(c4)), c4[3]));

    for (var j=0; j<3; j++) {
        normalOfPoints.push(vec4(cross(subtract(p2,p1), subtract(p3,p1)), 0));
    }
    for (var j=0; j<3; j++) {
        normalOfPoints.push(vec4(cross(subtract(p3,p1), subtract(p4,p1)), 0));
    }
    for (var j=0; j<3; j++) {
        normalOfPoints.push(vec4(cross(subtract(p2,p1), subtract(p4,p1)), 0));
    }
    for (var j=0; j<3; j++) {
        normalOfPoints.push(vec4(cross(subtract(p3,p2), subtract(p4,p2)), 0));
    }

    // 生成绘图索引
    indicesToDraw = indicesToDraw.concat([
        numOfPoints+0, numOfPoints+1, numOfPoints+2,
        numOfPoints+3, numOfPoints+4, numOfPoints+5,
        numOfPoints+6, numOfPoints+7, numOfPoints+8,
        numOfPoints+9, numOfPoints+10, numOfPoints+11,
    ]);

    // 返回坐标
    return pointsToDraw.slice(numOfPoints).concat(normalOfPoints.slice(numOfPoints));
}

function mult4object(R, P) { // P = RP
    for (var n=0; n<P.length/2; n++) {
        var p = vec4(P[n]);
        for (var i=0; i<3; i++) {
            P[n][i] = 0;
            for (var j=0; j<4; j++) {
                P[n][i] += R[i][j]*p[j];
            }
        }
    }

    R = inverse(transpose(R));
    for (var n=P.length/2; n<P.length; n++) {
        var p = vec4(P[n]);
        for (var i=0; i<3; i++) {
            P[n][i] = 0;
            for (var j=0; j<4; j++) {
                P[n][i] += R[i][j]*p[j];
            }
        }
    }
}


function mult4Elements(M, v) {
    var R = [];
    for (var i=0; i<M.length; i++) {
        R.push([]);
        for (var j=0; j<v.length; j++) {
            R[i].push(M[i][j] * v[j]);
        }
    }

    return R;
}